using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace Hsenl.Network {
    public class PacketWriter {
        private const int MaxBufferSize = 1024 * 1024; // 当一个buffer的缓存被括的超过该值, 则进行警报, 看看发生什么事了
        private int _bodySizeBits;
        private int _totalHeadBits;

        private HBuffer _bufferSending = new(); // 两个send缓存区, 一个用来发送, 一个用来写入, 谁没在发送中, 谁当写入
        private HBuffer _bufferWriting = new();
        private bool _ensureMsgComplete;
        private Queue<int> _sendingPositions;

        public event Action<Memory<byte>> OnMessageWritedEvent; // 当写入了包(包括包装包)
        public event Action OutputEvent;

        private int _waitOutputDelay = 1; // ms
        private volatile int _waitingForOutput;
        private readonly object _swapLocker = new();
        private volatile int _lastFrameCount;

        public bool HasWaitSend => this._bufferSending.Position != 0 || this._bufferWriting.Position != 0;

        /// <param name="bodySizeBits"></param>
        /// <param name="totalHeadBits"></param>
        /// <param name="ensureMsgComplete">保证消息完整, 输出时, 不会被截断</param>
        public void Init(int bodySizeBits, int totalHeadBits, bool ensureMsgComplete = false) {
            this._bodySizeBits = bodySizeBits;
            this._totalHeadBits = totalHeadBits;
            this._ensureMsgComplete = ensureMsgComplete;
            if (ensureMsgComplete) {
                this._sendingPositions = new();
            }
        }

        public void Update() {
            if (this.HasWaitSend) {
                this.Output();
            }
        }

        private void Output() {
            lock (this._swapLocker) {
                Interlocked.Exchange(ref this._lastFrameCount, -1);
                try {
                    this.OutputEvent?.Invoke();
                }
                catch (Exception e) {
                    Log.Error(e);
                }
            }
        }

        // 粘包
        public void Write(byte[] data, int offset, int count, bool autoOutput) {
            this.Write(data.AsSpan(offset, count), autoOutput);
        }

        private long _lastWriteTimestamp;

        public long GetWriteInterval() {
            var now = TimeInfo.GetTickMs();
            var interval = now - Volatile.Read(ref this._lastWriteTimestamp);
            return interval;
        }

        public int UpdateFrameDiff() {
            if (this._lastFrameCount == -1)
                return -1;

            var now = TimeInfo.FrameCount;
            var diff = now - this._lastFrameCount;
            return diff;
        }

        public void Write(Span<byte> msg, bool autoOutput) {
            try {
                lock (this._swapLocker) {
                    var msglen = (ushort)msg.Length;
                    if (msglen == 0)
                        return;

                    if (this._bufferWriting.Length > MaxBufferSize) {
                        // todo 如果缓存区写入过大, 可能因为意外原因, 导致积压的数据迟迟没有发出去, 这时先暂停写入, 后续根据实际情况, 修改是否报错
                        Log.Error("Writer buffer too large, temporarily stop writing, please send in time!");
                        return;
                    }

                    this._bufferWriting.GetSpan(this._bodySizeBits).WriteTo(msglen); // 写入消息体长度
                    this._bufferWriting.Advance(this._totalHeadBits);
                    var start = this._bufferWriting.Position;
                    this._bufferWriting.Write(msg); // 写入消息体
                    var memory = this._bufferWriting.AsMemory(start, msglen);
                    var pos = this._bufferWriting.Position;

                    if (this._ensureMsgComplete) {
                        this._sendingPositions.Enqueue(pos);
                    }

                    try {
                        this.OnMessageWritedEvent?.Invoke(memory);
                    }
                    catch (Exception e) {
                        Log.Error(e);
                    }
                }
            }
            finally {
                if (autoOutput) {
                    // 方案 1
                    if (Interlocked.CompareExchange(ref this._waitingForOutput, 1, 0) == 0) {
                        OutputTimer.Start(this);
                    }
                
                    // 方案 2
                    // Interlocked.Exchange(ref this._lastWriteTimestamp, TimeInfo.GetTickMs());
                    // if (Interlocked.CompareExchange(ref this._waitingForOutput, 1, 0) == 0) {
                    //     Interlocked.Exchange(ref this._lastFrameCount, TimeInfo.FrameCount);
                    //     OutputTimer2.Start(this);
                    // }
                }
            }
        }

        public Memory<byte> GetSendBuffer(int length) {
            var bytes = this.GetSendBuffer(length, out var offset, out var count);
            return bytes.AsMemory(offset, count);
        }

        public byte[] GetSendBuffer(int length, out int offset, out int count) {
            var len = this._bufferSending.Position;

            // len == 0, 说明没有数据要发了
            if (len == 0) {
                lock (this._swapLocker) {
                    if (this._bufferWriting.Position != 0) {
                        ObjectHelper.Swap(ref this._bufferSending, ref this._bufferWriting);
                        len = this._bufferSending.Position;
                    }
                    else {
                        offset = 0;
                        count = 0;
                        return null;
                    }
                }
            }

            if (len > length) {
                len = length;
            }

            if (this._ensureMsgComplete) {
                var origin = this._bufferSending.Origin;
                var tutallen = this._bufferSending.Position + origin;
                var last = false;
                while (true) {
                    if (!this._sendingPositions.TryPeek(out var pos))
                        break;

                    if (pos == tutallen) {
                        last = true;
                    }

                    pos -= this._bufferSending.Origin;

                    if (pos <= length) {
                        this._sendingPositions.Dequeue();
                        len = pos;

                        if (last)
                            break;
                    }
                    else {
                        break;
                    }
                }
            }

            offset = this._bufferSending.Origin;
            count = len;
            var buffer = this._bufferSending.GetBuffer();
            this._bufferSending.Origin += len;

            if (this._bufferSending.Position <= 0) {
                // 所有数据都发送完了, 重置buffer
                this._bufferSending.Origin = 0;
                this._bufferSending.Seek(0, SeekOrigin.Begin);
            }

            return buffer;
        }

        public void Dispose() {
            this._bodySizeBits = 0;
            this._totalHeadBits = 0;
            this._bufferSending?.Reset();
            this._bufferWriting?.Reset();
            this.OnMessageWritedEvent = null;
            this.OutputEvent = null;
            this._sendingPositions?.Clear();
            this._sendingPositions = null;
        }

        private static class OutputTimer {
            static OutputTimer() {
                new Thread(WorkLoop) {
                    IsBackground = true,
                    Name = "PacketWriter OutputTimer Worker"
                }.Start();
            }

            public static bool Running = true;
            private static readonly AutoResetEvent _queuedEvent = new(false);
            private static readonly ConcurrentQueue<(long, PacketWriter)> _queues = new();

            private static void WorkLoop() {
                while (Running) {
                    _queuedEvent.WaitOne(1);
                    while (_queues.TryDequeue(out (long timestamp, PacketWriter writer) value)) {
                        var now = TimeInfo.GetTickMs();
                        var writer = value.writer;
                        if (now - value.timestamp > writer._waitOutputDelay) {
                            writer.Output();
                            Interlocked.Exchange(ref writer._waitingForOutput, 0);
                            continue;
                        }

                        _queues.Enqueue(value);
                    }
                }
            }

            public static void Start(PacketWriter writer) {
                var timestamp = TimeInfo.GetTickMs();
                _queues.Enqueue((timestamp, writer));
                _queuedEvent.Set();
            }
        }

        private static class OutputTimer2 {
            static OutputTimer2() {
                new Thread(WorkLoop) {
                    IsBackground = true,
                    Name = "PacketWriter OutputTimer Worker"
                }.Start();
            }

            public static bool Running = true;
            private static readonly AutoResetEvent _queuedEvent = new(false);
            private static readonly ConcurrentQueue<PacketWriter> _queues = new();

            private static void WorkLoop() {
                while (Running) {
                    _queuedEvent.WaitOne(1);
                    while (_queues.TryDequeue(out var writer)) {
                        if (writer.UpdateFrameDiff() > 0 || writer.GetWriteInterval() > 1) {
                            writer.Output();
                            Interlocked.Exchange(ref writer._waitingForOutput, 0);
                            continue;
                        }

                        _queues.Enqueue(writer);
                    }
                }
            }

            public static void Start(PacketWriter writer) {
                _queues.Enqueue(writer);
                _queuedEvent.Set();
            }
        }
    }
}